package com.lcf.framework.utils;

import cn.idev.excel.FastExcel;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import cn.idev.excel.exception.ExcelDataConvertException;
import cn.idev.excel.read.listener.PageReadListener;
import cn.idev.excel.read.listener.ReadListener;
import com.lcf.framework.exception.SysException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * excel读取
 *
 * @author lcf
 */
public class ExcelReadUtils {
    /**
     * 分批处理数据
     *
     * @param inputStream 文件流
     * @param clazz       实体类型
     * @param batchCount  批量处理的条数
     * @param consumer    处理数据的回调方法
     */
    public static <T> void readBatch(InputStream inputStream, Class<T> clazz, int batchCount, Consumer<List<T>> consumer) {
        FastExcel.read(inputStream, clazz, new PageReadListener<>(consumer, batchCount)).sheet().doRead();
    }

    /**
     * 处理全部数据
     *
     * @param inputStream 文件流
     * @param clazz       实体类型
     * @param consumer    处理数据的回调方法
     */
    public static <T> void read(InputStream inputStream, Class<T> clazz, Consumer<List<T>> consumer) {
        FastExcel.read(inputStream, clazz, new UploadDataListener<>(consumer)).sheet().doRead();
    }

    /**
     * 处理全部数据
     *
     * @param inputStream 文件流
     * @param clazz       实体类型
     */
    public static <T> void read(InputStream inputStream, Class<T> clazz, ReadListener<T> readListener) {
        FastExcel.read(inputStream, clazz, readListener).sheet().doRead();
    }

    /**
     * 不定义监听器读取一个简单的 Excel 文件，适用于数据量较小的场景
     *
     * @param inputStream 文件流
     * @param clazz       实体类型
     */
    public static <T> List<T> read(InputStream inputStream, Class<T> clazz) {
        // 使用 FastExcel 同步读取 Excel 数据为对象列表
        return FastExcel.read(inputStream).head(clazz).sheet().doReadSync();
    }

    /**
     * 分批处理数据
     *
     * @param inputStream   文件流
     * @param clazz         实体类型
     * @param batchCount    批量处理的条数
     * @param headRowNumber 表头行数 默认1
     * @param consumer      处理数据的回调方法
     */
    public static <T> void readBatch(InputStream inputStream, Class<T> clazz, int batchCount, int headRowNumber, Consumer<List<T>> consumer) {
        FastExcel.read(inputStream, clazz, new PageReadListener<>(consumer, batchCount)).sheet().headRowNumber(headRowNumber).doRead();
    }

    /**
     * 处理全部数据
     *
     * @param inputStream   文件流
     * @param clazz         实体类型
     * @param headRowNumber 表头行数 默认1
     * @param consumer      处理数据的回调方法
     */
    public static <T> void read(InputStream inputStream, Class<T> clazz, int headRowNumber, Consumer<List<T>> consumer) {
        FastExcel.read(inputStream, clazz, new UploadDataListener<>(consumer)).sheet().headRowNumber(headRowNumber).doRead();
    }

    /**
     * 处理全部数据
     *
     * @param inputStream   文件流
     * @param clazz         实体类型
     * @param headRowNumber 表头行数 默认1
     */
    public static <T> void read(InputStream inputStream, Class<T> clazz, int headRowNumber, ReadListener<T> readListener) {
        FastExcel.read(inputStream, clazz, readListener).sheet().headRowNumber(headRowNumber).doRead();
    }

    /**
     * 不定义监听器读取一个简单的 Excel 文件，适用于数据量较小的场景
     *
     * @param inputStream   文件流
     * @param clazz         实体类型
     * @param headRowNumber 表头行数 默认1
     */
    public static <T> List<T> read(InputStream inputStream, Class<T> clazz, int headRowNumber) {
        // 使用 FastExcel 同步读取 Excel 数据为对象列表
        return FastExcel.read(inputStream).head(clazz).sheet().headRowNumber(headRowNumber).doReadSync();
    }

    /**
     * 自定义监听器
     */
    @Slf4j
    private static class UploadDataListener<T> extends AnalysisEventListener<T> {
        private final List<T> list = new ArrayList<>();
        private final Consumer<List<T>> consumer;

        public UploadDataListener(Consumer<List<T>> consumer) {
            this.consumer = consumer;
        }

        @Override
        public void onException(Exception exception, AnalysisContext context) throws Exception {
            if (exception instanceof ExcelDataConvertException ex) {
                String msg = String.format("第 %d 行, 第 %d 列解析异常, 数据: %s", ex.getRowIndex(), ex.getColumnIndex(), ex.getCellData());

                throw new SysException(msg);
            }

            throw exception;
        }

        @Override
        public void invoke(T data, AnalysisContext context) {
            list.add(data);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            if (CollectionUtils.isNotEmpty(this.list)) {
                this.consumer.accept(this.list);
            }
        }
    }
}
